package com.someclip.utils.drag
{
	import com.someclip.utils.drag.core.DragReferenceType;
	import com.someclip.utils.drag.core.ICloneAble;
	import com.someclip.utils.drag.core.IDragEffectAcceptAble;
	import com.someclip.utils.drag.core.IDragObjectAcceptAble;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.getDefinitionByName;

	/**
	 * 拖拽管理器，可以实现拖拽对象，对象的图标，数据。
	 * 如要拖拽通过对象生成的位图，可以设置关联方式为DRAW；
	 * 如果只拖拽数据，可以在调用时提供数据对象，并在接受方做相应的接受处理。
	 *
	 * 默认对齐方式是以鼠标注册点为拖拽中心，如果需要改变，请提供dragCenter，
	 * 跟据对象的坐标系指定的一个点。
	 *
	 * 管理器提供对dragover和dragout效果的处理，如果需要处理这些效果可以在接受方
	 * 和被拖拽方实现IDragEffectAcceptAble接口。可以全局设置关闭效果，调用时只需要设置
	 * closeEffect为true;
	 *
	 * @author Argus
	 * @version 1.0
	 */
	public class SCDragManager
	{
		/**
		 * 拖拽的位图对象
		 */
		private var _image:Bitmap;
		/**
		 * 拖拽对象
		 */
		private var _target:DisplayObject;
		/**
		 * 拖拽的可视对象
		 */
		private var _visual:DisplayObject;

		/**
		 * 装载拖拽可视对象的容器
		 */
		private var _visualHolder:DisplayObjectContainer;
		/**
		 * 拖拽中心
		 */
		private var _center:Point;

		/**
		 * 默认拖拽层
		 */
		public var defaultContainer:DisplayObjectContainer;
		/**
		 * 拖拽层
		 */
		private var _container:DisplayObjectContainer;
		/**
		 * 是否关闭效果
		 */
		private var _closeEffect:Boolean;
		/**
		 * 是否拖拽离开限制区域时理解为释放
		 */
		private var _outToStop:Boolean;

		/**
		 * 拖拽的限制区域，以container为参考，如果container为null，则以defaultContainer为参考
		 */
		private var _restrict:Rectangle;
		/**
		 * 拖拽的状态
		 */
		private var _isDraging:Boolean=false;

		private var _referenceType:String;

		private var _releaseHandler:Function;

		/**
		 * 释放点
		 */
		private var _dropPoint:Point=new Point();

		private var _projectType:String;
		private static var _ins:SCDragManager;

		public function SCDragManager(single:DragManagerSingleton)
		{
			if (_ins)
				throw new Error("Singleton Error:SCDragManager.instance获取实例!");
		}

		/**
		 * 获取拖拽组件的实例
		 * @return
		 *
		 */
		public static function get instance():SCDragManager
		{
			if (!_ins)
				_ins=new SCDragManager(new DragManagerSingleton());
			return _ins;
		}

		/**
		 * 拖拽调用方法
		 * @param	target 拖拽的目标
		 * @param 	container 拖拽层
		 * @param   center 拖拽的中心，以target的visual注册点为参考
		 * @param   closeEffect 定义是否关闭drag效果
		 * @param	restrict 限制范围，Rectangle
		 * @param   outToStop 定义是否在鼠标移出restrict时停止拖拽
		 */
		public function doDrag(target:DisplayObject, referenceType:String="direct", releaseHandler:Function=null, container:DisplayObjectContainer=null, center:Point=null, restrict:Rectangle=null, outToStop:Boolean=false, closeEffect:Boolean=true):void
		{

			if (_isDraging)
				return;
			_isDraging=true;
			_target=target;
			_referenceType=referenceType;
			_container=container;
			_center=center;
			_outToStop=outToStop;
			_restrict=restrict;
			_closeEffect=closeEffect;
			if (!_target)
			{
				throw new Error("SCDragManager:要拖拽总需要个对象的吧？target怎么能为null呢？");
			}
			initContainer();
			setupTarget();
			startUp();
		}

		private function initContainer():void
		{
			if (_container == null)
				_container=defaultContainer;
			if (_container == null)
				throw new Error("无法获得拖拽层，请保证defaultContainer或container都不会null");
		}

		private function setupTarget():void
		{
			//设置偏移中心
			if (_center == null)
			{
				_center=new Point(_target.mouseX, _target.mouseY);
			}
			//构建拖拽用的可视对象
			switch (_referenceType)
			{
				case DragReferenceType.DRAW:
					createBitmap();
					_visual=_image;
					break;
				case DragReferenceType.DIRECT:
					_visual=_target;
					break;
				case DragReferenceType.CLONE:
					_visual=getClone();
					break;
				default:
					_visual=_target;
					break;
			}
			/*检查项目类型，如果是flex项目自动构建UIComponent为容器装拽对象*/
			try
			{
				_visualHolder=new Sprite();
				_container.addChild(_visualHolder);
				_projectType="as";
			}
			catch (e:TypeError)
			{
				_visualHolder=null;
				var cls:Class=getDefinitionByName("mx.core.UIComponent") as Class;
				_visualHolder=new cls();
				_container.addChild(_visualHolder);
				_projectType="mx"
			}
			_visualHolder.addChild(_visual);
			_visual.x=0;
			_visual.y=0;
			if (_projectType == "mx")
			{
				_visualHolder.width=_visual.width;
				_visualHolder.height=_visual.height;
			}
			_visualHolder.mouseChildren=false;
			_visualHolder.mouseEnabled=false;
			//初始化位置
			mouseMoveHandler();
		}

		private function getClone():DisplayObject
		{
			if (_target is ICloneAble)
			{
				return (_target as ICloneAble).clone() as DisplayObject;
			}
			else
			{
				throw new Error("要使用DragReferenceType.CLONE关联方式，target请先实现ICloneAble");
			}
			return null;
		}

		//绘制图标
		private function createBitmap():void
		{
			if (!_image)
				_image=new Bitmap();
			var bound:Rectangle=_target.getRect(_container);
			bound.width=Math.ceil(bound.width);
			bound.height=Math.ceil(bound.height);
			var bmd:BitmapData=new BitmapData(bound.width, bound.height, true);
			var matrix:Matrix=new Matrix();
			bmd.draw(_target, matrix);
			_image.bitmapData=bmd;
			bmd=null;
			matrix=null;
			bound=null;
		}

		private function startUp():void
		{
			//注册拖拽过程中的所有监听
			_container.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false, 0, true)
			_container.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false, 0, true);
			_container.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler, false, 0, true);
			_container.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler, false, 0, true);
		}

		private function mouseOverHandler(e:MouseEvent):void
		{
			if (_closeEffect)
				return;
			//寻找接受效果对象，并处理效果
			var effectTarget:*=getEffectAbleTarget(e.target);
			if (effectTarget)
			{
				(effectTarget as IDragEffectAcceptAble).handleDragOver();
				if (_target is IDragEffectAcceptAble)
				{
					(_target as IDragEffectAcceptAble).handleDragOver();
				}
			}
			effectTarget=null;

		}

		private function mouseOutHandler(e:MouseEvent):void
		{
			//根据目标是否支持DragEffect，告知目标处理DragEffect
			if (_closeEffect)
				return;
			//寻找效果接受对象，并处理效果
			var effectTarget:*=getEffectAbleTarget(e.target);
			if (effectTarget)
			{
				(effectTarget as IDragEffectAcceptAble).handleDragOut();
				if (_target is IDragEffectAcceptAble)
				{
					(_target as IDragEffectAcceptAble).handleDragOut();
				}
			}
			effectTarget=null;
		}

		/**
		 * 自对象本身开始，往上遍历显示列表，寻找最接近的效果接受对象，
		 * 并检查效果接受对象所能接受的对象是否当前对象
		 * @param	currentTarget 当前鼠标对象
		 * @return 符合条件的效果接受对象，寻找不到返回null;
		 */
		private function getEffectAbleTarget(currentTarget:*):*
		{
			var desireTarget:Class;
			while (currentTarget)
			{
				if (currentTarget is IDragEffectAcceptAble)
				{
					//从效果接受对象获取可接受的对象类。Class
					desireTarget=(currentTarget as IDragEffectAcceptAble).effectTarget;
					//判断对象或者数据是否满足要求
					if (desireTarget && (_target is desireTarget))
					{
						return currentTarget;
					}
				}
				if (currentTarget.parent)
				{
					currentTarget=currentTarget.parent
				}
				else
				{
					currentTarget=null;
				}
			}
			return null;
		}

		/**
		 * 自当前对象本身开始，往上遍历显示列表，寻找最近一个接受拖拽对象的容器，
		 * @param	currentTarget 当前鼠标对像
		 * @return 寻找到的接受拖拽对象的容器，找不到返回null
		 */
		private function getAcceptAbleTarget(currentTarget:*):*
		{
			var desireTargets:Array;
			while (currentTarget)
			{
				if (currentTarget is IDragObjectAcceptAble)
				{
					//从拖拽接受对象获取可接受的对象类。Class
					//desireTargets=(currentTarget as IDragObjectAcceptAble).acceptTargets;
					//判断对象或者数据是否满足要求。
					/*if (isIn(desireTargets))
					{*/
					return currentTarget;
					/*}*/
				}
				if (currentTarget.parent)
				{
					currentTarget=currentTarget.parent
				}
				else
				{
					currentTarget=null;
				}
			}
			return null;
		}

		private function isIn(desireTargets:Array):Boolean
		{
			if (desireTargets == null || desireTargets.length == 0)
				return false;
			for each (var obj:Class in desireTargets)
			{
				if (_target is obj)
				{
					return true;
				}
			}
			return false;
		}

		private function mouseUpHandler(e:MouseEvent, out:Boolean=false):void
		{
			//拖拽结束，移除监听
			_container.removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler, false)
			_container.removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler, false);
			_container.removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler, false);
			_container.removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler, false);
			//自对象开始往上遍历所有容器，寻找最近的接受drag对象的容器，
			//并访问其handleDropObject方法,处理drop事件
			var state:Boolean=false;
			var acceptAbleTarget:*=getAcceptAbleTarget(e.target);
			if (acceptAbleTarget)
			{
				_dropPoint.x=_visualHolder.x;
				_dropPoint.y=_visualHolder.y;
				_dropPoint=_container.localToGlobal(_dropPoint);
				_dropPoint=(acceptAbleTarget as DisplayObject).globalToLocal(_dropPoint);
				(acceptAbleTarget as IDragObjectAcceptAble).handleDropObject(_target, _dropPoint);
			}
			//acceptAbleTarget=null;
			//移除效果
			mouseOutHandler(e);
			//回调拖拽结束方法，并告知拖拽对象的处理状态。被告知方可以作出相应处理。
			if (_releaseHandler != null)
			{
				if (out)
				{
					_releaseHandler(-1);
				}
				else
				{
					if (acceptAbleTarget != null)
					{
						_releaseHandler(1);
					}
					else
					{
						_releaseHandler(-1);
					}
				}
			}
			//销毁过程中的所有对象。
			destory();
			_isDraging=false;

		}

		private function destory():void
		{
			//销毁所有对象及引用
			if (_container.contains(_visualHolder))
				_container.removeChild(_visualHolder);
			if (_visualHolder.contains(_visual))
				_visualHolder.removeChild(_visual);
			_visualHolder=null;
			_visual=null;
			if (_image)
			{
				_image.bitmapData.dispose();
				_image=null;
			}
			_target=null;
			_container=null;
			_center=null;
		}

		private function mouseMoveHandler(e:MouseEvent=null):void
		{
			//计算位置并更新
			var mx:Number=_container.mouseX;
			var my:Number=_container.mouseY;
			if (_outToStop && _restrict != null && !_restrict.contains(mx, my))
			{
				mouseUpHandler(e, false);
				return;
			}
			if (_center)
			{
				mx-=_center.x;
				my-=_center.y;
			}
			if (_restrict)
			{
				if (mx < _restrict.left)
					mx=_restrict.left;
				if (my < _restrict.top)
					my=_restrict.top;
				if (mx > _restrict.right - _visualHolder.width)
					mx=_restrict.right - _visualHolder.width;
				if (my > _restrict.bottom - _visualHolder.height)
					my=_restrict.bottom - _visualHolder.height;
			}
			_visualHolder.x=mx;
			_visualHolder.y=my;
		}

	}
}

class DragManagerSingleton
{
}
